#include "SWAPBase.h"
#include <PAL/MMU.h>
#include <BasicMatic.h>
#include <OSProc.h>
#include <MutexLock.h>
#include <structure.h>

#define listnode2  listnode<2>

#define STATIC_MEMORY_SIZE 512
#define STATIC_MEMORY_PER_SIZE sizeof(_length)

#define _GetProcess(len)  ((len).data_[_EXPECT_BYTES_LENGTH - 2])
#define _GetAddress(len)  ((len).data_[_EXPECT_BYTES_LENGTH - 1])
#define _CheckMapped(len) (_GetProcess(len) != _STATIC_MEMORY)
#define SWAPbegin         Makelength<_STATIC_MEMORY,0>()

#define _STATIC_MEMORY    (~0)

#define _maprecord  map<_length,_length>

namespace SWAP
{
  //  typedef map<_length,_length> _maprecord;
  inline int  RecordToMap(const _length& baseoffset,const _length& offset)
  {
    //if the SWAP out is SWAP itself, record on the ROOT
    if(_GetProcess(offset) == NOW)
      {
	
	RecordToMap_SWAP(baseoffset,offset,FindStream("SWAP",ROOT));
	
      }
    else
      {
	//It's a Swap out operating on MapRecord.
	//it's a normal SWAP in or a static memory. 
	RecordToMap_Map(baseoffset,offset);
	
      }
 
  }

  inline int UnrecordToMap(const _length& baseoffset,const _length& offset)
  {
    if(_GetProcess(offset) == NOW)
      {
	
	UnrecordToMap_SWAP(baseoffset,offset,FindStream("SWAP",ROOT));

      }
    else 
      {
	//it's a Swap out operating on MapRecord
	//it's a normal SWAP in or a static memory. 
	UnrecordToMap_Map(baseoffset,offset);
      }
    
  }
  inline _length GetRecord(_length offset)// Problem.. 
  {
    _length ret;
    _maprecord::iterator it;
    return (_CheckMapped(ret =  
			 (((it = MapRecord->find(offset)) != _maprecord.end())
			  ?(*it)
			  : (__KERNEL_PANIC(0),Makelength(0))))
	    ? ret
	    : GetRecord(ret));
  }
}



static int RecordToMap_SWAP(const _length& baseoffset,const _length& offset, _fstr SWAProot)
{
 
  _fstr swapout = CreateStream([](unsigned int size)->_fstr{return KMalloc(size)});
  char _offset[sizeof(_length)+1] = {'\0'};
  unsigned int* _baseoffset = KMalloc(sizeof(_length));
  _Memcpy(offset,_offset,sizeof(_length));
  _Memcpy(baseoffset,_baseoffset,sizeof(_length));
  
  NameStream(_offset,swapout);
  swapout->object = _baseoffset;  //Will change -- until fileStream give a proper API
  MountStream(swapout,SWAProot);
}

static int UnrecordToMap_SWAP(const _length& baseoffset,const _length& offset,_fstr SWAProot)
{
  char _offset[sizeof(_length)+1] = {'\0'};
  //  unsigned int* _baseoffset = KMalloc(sizeof(_length));
  _Memcpy(offset,_offset,sizeof(_length));
  //  _Memcpy(baseoffset,_baseoffset,sizeof(_length));
  _fstr swapin = FindStream(_offset,SWAProot);
  UnmountStream(swapin,SWAProot);
}

static int RecordToMap_Map(const _length& baseoffset,const _length& offset)
{
  //Initialize map
  if(MapRecord == NULL)
    {
      MapRecord = _Malloc(sizeof(_maprecord));
      MapRecord->_maprecord ([](_ptlong size)->_ptlong*{return _Malloc(size);});
    }
  _maprecord::iterator buff = MapRecord->find(offset);
  if((buff) == _maprecord.end())
    {//Normal Memory
      MapRecord->insert(_maprecord::value_type(offset,baseoffset));
    }
  else
    {//Static Memory
      MapRecord->insert(_maprecord::value_type(*buff,baseoffset));
      stminfo* info = _GetAddress(*buff);
      info->phyMem = NULL;
      STMINFO_TRAVERSAL(info,
			[](listnode2* node)->int
			{
			  MMUMemorySet(((_pcb*)(node->pro_addr->mark[0]))->flPage,(_ptlong*)(node->pro_addr->mark[1]),SWAP_OUT);
			  return 1;
			});
      
    }
}

static int UnrecordToMap_Map(const _length& baseoffset,const _length& offset)
{
  if(MapRecord == NULL)
    {
      MapRecord = _Malloc(sizeof(_maprecord));
      MapRecord->_maprecord ([](_ptlong size)->_ptlong*{return _Malloc(size);});
    }

  _maprecord::iterator buff = MapRecord->find(offset);
  if(buff == _maprecord.end())
    return 0;
  _maprecourd::iterator buff;
  if(*(buff) == baseoffset)
    {//Normal Memory
      MapRecord->erase(buff);
    }
  else
    {//Static Memory
      stminfo* info = *buff;
      if((buff = MapRecord->find(*buff)) != _maprecord.end())
	{//Swapouted Static Memory
	  MapRecord->erase(buff);
	  info->phyMem = baseoffset;
	  STMINFO_TRAVERSAL(info,
			    [](listnode2* node)->int
			    {
			      MMUMemoryMap(((_pcb*)(node->pro_addr->mark[0]))->flPage,baseoffset,(_ptlong*)(node->pro_addr->mark[1]),NORMAL);
			      return 1;
			    });
	}
      else
	{
	  return 0;
	}
    }
  
}

#include "SWAPInterface.h"

/*
static inline int StaticMemoryPush(const _length& offset)
{
  if(StaticBasequeue == NULL)
    {
	SWAPBasequeue = _Malloc(sizeof(Spareoffsetqueue));
	SWAPBasequeue->Spareoffsetqueue(STATIC_MEMORY_PER_SIZE,STATIC_MEMORY_SIZE,SWAPbegin);
      }
    return SWAPBasequeue->Push(offset);

}


static inline _length StaticMemoryPop()
{
  if(StaticBasequeue == NULL)
    {
	SWAPBasequeue = _Malloc(sizeof(Spareoffsetqueue));
	SWAPBasequeue->Spareoffsetqueue(STATIC_MEMORY_PER_SIZE,STATIC_MEMORY_SIZE,SWAPbegin);
      }
    return SWAPBasequeue->Pop();
  
}
*/
using namespace PAL;
using namespace SWAP;
using namespace MM;

static inline stminfo* STMINFO_GIVE(stminfo* info,_pcb* pro,unsigned int* vAddr)
{
  listnode2* newnode = MakeListNode(_KMalloc(sizeof(listnode2)),NULL);
  newnode->mark[0] = pro;
  newnode->mark[1] = vAddr;   
  InsertListNode(info->pro_addr,newnode);
}
static inline stminfo* STMINFO_INIT(stminfo* info,_pcb* pro,unsigned int* vAddr)
{
   info = _KMalloc(sizeof(stminfo));
   info->phyMem = NULL;
   info->pro_addr = MakeListNode(_KMalloc(sizeof(listnode2)),NULL);
   InsertListNode(info->pro_addr,MakeListNode(_Malloc(sizeof(listnode2)),NULL));
   info->pro_addr->next->mark[0] = pro;
   info->pro_addr->next->mark[1] = vAddr;   
}
static inline listnode2* STMINFO_FIND(stminfo* info,const _length& pro_addr)
{
  return STMINFO_TRAVERSAL(info,[](listnode2* node)->int{return Comparelength(Makelength((_ptlong)(node->mark[0]),(_ptlong)(node->mark[1])),pro_addr);};
}

static inline listnode2* STMINFO_TRAVERSAL(stminfo* info,int (*fun)(listnode2*))
{
  listnode2* node = info->pro_addr;
  while(node != NULL)
    {
      if(fun(node) == 0)
	break;
      node = node->next;
    }
  return node;
}

#include "StaticMemory.h"


using namespace StaticMemory;

  stminfo* StaticMemorize(_pcb* proA,_ptlong* vAddrA,_pcb* proB, _ptlong* vAddrB)
  {
    _length A = Makelength((_ptlong)proA,(_ptlong)vAddrA);
    if(MapRecord == NULL)
    {
      MapRecord = _Malloc(sizeof(_maprecord));
      MapRecord->_maprecord ([](_ptlong size)->_ptlong*{return _Malloc(size);});
    }
      
    //_length B = Makelength((_ptlong)proB,(_ptlong)vAddrB);
    _maprecord::iterator buff = MapRecord->find(A);
    //unsigned int* phyMem;
    stminfo* info;
    if(buff == _maprecord.end() || _CheckMapped(*buff))  //if the first one is true , the second one won't be checked . <- Am i right?
      {//A hasn't been StaticMemorize //Create A info & map
	info = STMINFO_INIT(info, proA,vAddrA);
	//	_length stm = Makelength((_ptlong)STATIC_MEMORY,(_ptlong)info);
	info.phyMem = (buff == _maprecord.end()
		       ? ( MMUGetPMem(proA->flPage,vAddrA))
		       : (MapRecord->insert(_maprecord::value_type(stm,*buff))
			  ,MapRecord->erase(buff)
			  ,MapRecord->insert(_maprecord::value_type(A,stm))
			  ,NULL));	
      }
    else //Has already been StaticMemorized
      {
	info = *buff;
      }
      //Create B info
 	
    
    return StaticMemorize(info,proB,vAddrB);

  }
  inline stminfo* StaticMemorize(stminfo* info,_pcb* pro,_ptlong* vAddr)
  {//No checking info -- No API use
    if(pro == NULL)
      return info;
    _length B = Makelength(pro,vAddr);
    _maprecord::iterator buff;
    STMINFO_GIVE(info,pro,vAddr)
    if( (buff = MapRecord->find(B)) != _maprecord.end())
      {
	PushSpareOffset(*buff);
	UnrecordToMap_Map(*buff,B);
      }
    else
      {
	unsigned int* BPmem = MMUGetPMem(proB->flPage,vAddrB);
	if(BPmem != NULL)
	  PushAbandonedMemory(BPmem);
      }
    MapRecord->insert(_maprecord::value_type(B,Makelength(_STATIC_MEMORY,(_ptlong)info)));
    
    //Map B to A
    MMUMemoryEqual((_ptlong)(info->pro_addr->mark[0]),(_ptlong)(info->pro_addr->mark[1]),pro->flPage,vAddr);
    
    
    return info;
    
  }
  int inStaticMemorize(_pcb* pro,_ptlong* vAddr)
  {
    
    _length a = Makelength((_ptlong)pro,(_ptlong)vAddr);
    _maprecord::iterator buff;
    if(MapRecord == NULL)
    {
      MapRecord = _Malloc(sizeof(_maprecord));
      MapRecord->_maprecord ([](_ptlong size)->_ptlong*{return _Malloc(size);});
    }
  
    if((buff = MapRecord->find(a)) == _maprecord.end())
      return 0;
    if(_CheckMapped(a = *buff))
      return 0;
    stminfo* info = _GetAddress(a);
    return inStaticMemorize(info,pro,vAddr);
  }
  inline int inStaticMemorize(stminfo* info,_pcb* pro,_ptlong* vAddr)
  {
    int i = 1;
    _length a = Makelength((_ptlong)pro,(_ptlong)vAddr);
    _maprecord::iterator buff = MapRecord->find(a);
    RemoveListNode(STMINFO_FIND(info,a));
    if(info->pro_addr->next == NULL)  //Allow only one process in use of Static Memory
      {
	_KFree(info->pro_addr);
	_KFree(info);
	i = 2;
      }
    MapRecord->erase(buff);
    return i;
  }
  //The function is kept.
  /*
  inline unsigned int* GetStaticMemoryPhymem(_pcb* pro,unsigned int* vAddr) 
  // Design problem --the location of stminfo is recorded in SWAP memory- which might be swapped out. 
  {
    _maprecord::iterator it;
    return ((it = MapRecord->find(Makelength((_ptlong)pro,(_ptlong)vAddr))) != _maprecord.end()
	    ? (_GetAddress(*it))
	    : NULL);
  }
  */


